#include "EventLoop.h"
#include "Selector.h"

#include <assert.h>
#include <iostream>

namespace nio {

	void EventLoop::startup() {
		if (this->isRunning) {
			assert(false);
			return;
		}
		std::thread t;
		this->isRunning = true;
		this->thread = std::make_shared<std::thread>([this]() {
			this->loop();
			});
	}

	void EventLoop::shutdown() {
		assert(!this->inCurrentLoop());
		if (!this->isRunning) {
			assert(false);
			return;
		}
		this->isRunning = false;
		this->selector.wakeup();
		this->thread->join();
		while (!this->tasks.empty())this->tasks.pop();
	}

	void EventLoop::loop() {
		while (isRunning)
		{
			int64_t timeout = 1000000;
			lock.lock();
			if (!tasks.empty())timeout = tasks.top()->RemainTime();
			lock.unlock();
			if (timeout < 0)timeout = 0;

			//waiting for events and timeout
			selector.waitForEvents(timeout);
			if (!isRunning)break;

			std::vector<std::shared_ptr<Task>> arrTask;
			lock.lock();
			while (!tasks.empty() && tasks.top()->RemainTime() <= 0) {
				arrTask.push_back(tasks.top());
				if (tasks.top()->tryFinish()) {
					tasks.pop();
				}
				else {
					auto task = tasks.top();
					tasks.pop();
					tasks.push(task);
				}
			}
			lock.unlock();

			for (auto& task : arrTask)task->Execute();
		}
	}

	bool EventLoop::inCurrentLoop() {
		if (this->thread == nullptr)return false;
		return this->thread->get_id() == std::this_thread::get_id();
	}

	void EventLoop::assertCurrentLoop() {
		assert(inCurrentLoop());
	}

	void EventLoop::execute(std::function<void()> work) {
		if (inCurrentLoop()) {
			work();
			return;
		}
		auto task = std::make_shared<Task>(0, false, work);
		lock.lock();
		tasks.push(task);
		lock.unlock();
		selector.wakeup();
	}

	std::shared_ptr<Task> EventLoop::execute(int64_t interval, bool repeated, std::function<void()> work) {
		auto task = std::make_shared<Task>(interval, repeated, work);
		lock.lock();
		tasks.push(task);
		lock.unlock();
		if (!inCurrentLoop()) {
			selector.wakeup();
		}
		return task;
	}

}